package com.tiger.admin.http.logs;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.tiger.admin.Db;
import com.tiger.admin.dto.PaginationResult;
import com.tiger.admin.http.logs.dto.AppVisitLogExportDto;
import com.tiger.admin.http.logs.dto.AppVisitLogPageDto;
import com.tiger.admin.http.logs.type.AppVisitLogRes;
import com.tiger.db.entity.App;
import com.tiger.db.entity.ExportTask;
import com.tiger.mongodb.entity.AppVisitLog;
import com.tiger.utils.Utils;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.bson.types.ObjectId;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class AppVisitLogService {
    private final short exportSize = 5000;
    private final MongoTemplate mongoTemplate;
    private final Db db;

    public AppVisitLogService(MongoTemplate mongoTemplate, Db db) {
        this.mongoTemplate = mongoTemplate;
        this.db = db;
    }


    public PaginationResult<AppVisitLogRes> page(AppVisitLogPageDto page) {
        long startTime = page.getStartTime();
        long endTime = page.getEndTime();

        PaginationResult<AppVisitLogRes> result = new PaginationResult<>();

        Criteria criteria = new Criteria();
        Sort sort = Sort.by(Sort.Direction.DESC, "_id"); // 升序


        if(page.getAppName() != null && !page.getAppName().isEmpty()) {
            App app = db.app.getOne(new LambdaQueryWrapper<App>().select(App::getId, App::getName).eq(App::getName, page.getAppName()));
            if(app == null) return result;
            criteria.and("appId").is(app.getId());
        }

        if(startTime != 0 && endTime != 0) {
            if(startTime == endTime) {
                criteria.and("time").is(startTime);
            }else {
                criteria.and("time").gte(startTime).lt(endTime);
            }
        }



        Query query = new Query(criteria);
        query.with(sort);


        result.setTotal(mongoTemplate.count(query, AppVisitLog.class));

        if(result.getTotal() > 0) {
            query.skip((page.getPageNo() - 1) * page.getPageSize()).limit(page.getPageSize());

            List<AppVisitLog> logs = mongoTemplate.find(query, AppVisitLog.class);
            if(!logs.isEmpty()) {
                List<Long> appIds = logs.stream().map(AppVisitLog::getAppId).collect(Collectors.toList());
                List<App> apps = db.app.list(new LambdaQueryWrapper<App>().select(App::getId, App::getName).in(App::getId, appIds));

                List<AppVisitLogRes> rows = new ArrayList<>();


                for(AppVisitLog log : logs) {
                    AppVisitLogRes row = new AppVisitLogRes();
                    BeanUtils.copyProperties(log, row);
                    Optional<App> app = apps.stream() // 将List转换为流
                            .filter(item -> Objects.equals(item.getId(), log.getAppId())).findFirst();

                    if(app.isPresent()) {
                        row.setAppName(app.get().getName());
                    }

                    row.setUserKey(Utils.maskUserAccount(log.getUserKey()));

                    rows.add(row);
                }

                result.setRows(rows);

            }
        }

        return result;
    }

    @Async
    public void export(long id, AppVisitLogExportDto dto, String filePath) throws IOException {
        String _id = null;
        long appId = 0;


        if(dto.getAppName() != null && !dto.getAppName().isEmpty()) {
            App app = db.app.getOne(new LambdaQueryWrapper<App>().select(App::getId, App::getName).eq(App::getName, dto.getAppName()));
            if(app == null) return;
            appId = app.getId();
        }

        while (true) {
            List<AppVisitLogRes> rows = query(dto, appId, _id);
            writeToExcel(filePath, 0, rows);
            int size = rows.size();
            if(size >= exportSize) {
                _id = rows.get(size - 1).getId();
            }else {
                break;
            }
        }

        LambdaUpdateWrapper<ExportTask> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(ExportTask::getId, id);
        updateWrapper.set(ExportTask::getStatus, 1);


        boolean r = db.exportTask.update(updateWrapper);

    }

    public List<AppVisitLogRes> query(AppVisitLogExportDto dto, long appId, String _id) {
        List<AppVisitLogRes> rows = new ArrayList<>();


        Criteria criteria = new Criteria();

        if(_id != null && !_id.isEmpty()) {
            criteria.and("_id").gt(new ObjectId(_id));
        }

        if(appId > 0) {
            criteria.and("appId").is(appId);
        }


        long startTime = dto.getStartTime();
        long endTime = dto.getEndTime();

        if(startTime > 0 || endTime > 0) {
            criteria.and("time");
            if(startTime == endTime) {
                criteria.is(startTime);
            }else {

                if(startTime > 0) {
                    criteria.gte(startTime);
                }

                if(endTime > 0) {
                    criteria.lt(startTime);
                }
            }
        }




        Query query = new Query(criteria);

        query.limit(exportSize);
        List<AppVisitLog> logs = mongoTemplate.find(query, AppVisitLog.class);

        if(!logs.isEmpty()) {
            List<Long> appIds = logs.stream().map(AppVisitLog::getAppId).collect(Collectors.toList());
            List<App> apps = db.app.list(new LambdaQueryWrapper<App>().select(App::getId, App::getName).in(App::getId, appIds));




            for(AppVisitLog log : logs) {
                AppVisitLogRes row = new AppVisitLogRes();
                BeanUtils.copyProperties(log, row);
                Optional<App> app = apps.stream() // 将List转换为流
                        .filter(item -> Objects.equals(item.getId(), log.getAppId())).findFirst();

                if(app.isPresent()) {
                    row.setAppName(app.get().getName());
                }

                rows.add(row);
            }

        }


        return rows;

    }

    /**
     * 向Excel文件的指定工作表写入多行数据。
     *
     * @param filePath    Excel文件路径
     * @param sheetIndex  工作表索引
     * @param data        要写入的数据，以二维数组的形式
     * @throws IOException 如果文件操作失败
     */
    public static void writeToExcel(String filePath, int sheetIndex, List<AppVisitLogRes> data) throws IOException {
        FileInputStream fis = new FileInputStream(new File(filePath));
        Workbook workbook = WorkbookFactory.create(fis);
        Sheet sheet = workbook.getSheetAt(sheetIndex); // 获取工作表

        // 计算要写入数据的起始行号
        int startRow = sheet.getLastRowNum() + 1;

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");

        // 遍历数据列表，逐行写入
        for (int i = 0; i < data.size(); i++) {
            AppVisitLogRes rowData = data.get(i);
            Row row = sheet.getRow(startRow + i); // 获取或创建行
            if (row == null) {
                row = sheet.createRow(startRow + i); // 如果行不存在，则创建新行
            }

            row.createCell(0).setCellValue(rowData.getAppName());
            row.createCell(1).setCellValue(rowData.getUserKey());
            row.createCell(2).setCellValue(sdf.format(new Date(rowData.getCreateTime() * 1000)));

        }

        // 将更改写回文件
        FileOutputStream fos = new FileOutputStream(filePath);
        workbook.write(fos);
        fos.close();

        // 关闭资源
        workbook.close();
        fis.close();
    }


    public AppVisitLog query(String id) {
        return mongoTemplate.findById(id, AppVisitLog.class);
    }
}
